
#include <BBuffer>
#include <BFolder>
#include <BEntry>
#include <BFile>
#include "member_BEntry.h"
#include "member_BFile.h"

using namespace BWE;

#define member (*(member_BFile *)_ptr)
#define member_allocate() _ptr = new member_BFile(this)
#define member_release() delete (member_BFile *)_ptr

bool BFile::Load(const BString &fileName, BByteArray &bytes)
{
	BFile file(fileName);
	if (file.open(IO_Read))
	{
		int size = file.size();
		bytes.resize(size);
		if (size == 0)
			return true;
		return file.read(bytes.data(), size);
	}
	return false;
}
bool BFile::Load(const BString &fileName, BBuffer &buffer)
{
	BFile file(fileName);
	if (file.open(IO_Read))
	{
		int size = file.size();
		buffer.resize(size);
		if (size == 0)
			return true;
		return file.read(buffer.bytes().data(), size);
	}
	return false;
}
bool BFile::Load(const BString &fileName, BString &text)
{
	BFile file(fileName);
	if (file.open(IO_ReadText))
	{
		int size = file.size();
		text.reserve(size);
		if (size == 0)
			return true;
		return file.read(text);
	}
	return false;
}
bool BFile::Save(const BString &fileName, const BByteArray &bytes)
{
	BFile file(fileName);
	if (file.open(IO_Write))
	{
		return file.write(bytes.data(), bytes.size());
	}
	return false;
}
bool BFile::Save(const BString &fileName, const BBuffer &buffer)
{
	BFile file(fileName);
	if (file.open(IO_Write))
	{
		const BByteArray &bytes = buffer.bytes();
		return file.write(bytes.data(), bytes.size());
	}
	return false;
}
bool BFile::Save(const BString &fileName, const BString &text)
{
	BFile file(fileName);
	if (file.open(IO_WriteText))
	{
		return file.write(text);
	}
	return false;
}

#ifdef linux

bool BFile::Exist(const BString &fileName)
{
	Attrib attrib = BEntry::Check(fileName);
	return attrib & Attrib_File;
}
bool BFile::Create(const BString &fileName, Attrib attrib)
{
	if (fileName.contain('/'))
	{
		BString path;
		int end = fileName.find('/', 1);
		while (end < fileName.size())
		{
			fileName.substr(path, 0, end);
			if (!BFolder::Exist(path))
			{
				if (0 != mkdir(path.cstr(), 0x0755))
					break;
			}
			end = fileName.find('/', end + 1);
		}
	}
	mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
	int fd = ::creat(fileName.cstr(), mode);
	if (fd == -1)
	{
		printf("create file : %s", fileName.cstr());
		return false;
	}
	::close(fd);
	return true;
}
bool BFile::Remove(const BString &fileName)
{
	int result = ::remove(fileName.cstr());
	return result == 0;
}
bool BFile::Rename(const BString &fileName, const BString &newName)
{
	if (fileName.contain('/'))
	{
		BString newFileName = fileName.path();
		newFileName << '/' << newName;
		int result = ::rename(fileName.cstr(), newFileName.cstr());
		return result == 0;
	}
	int result = ::rename(fileName.cstr(), newName.cstr());
	return result == 0;
}

FILE *BFile::FOpen(const BString &fileName, IOMode mode)
{
	if (fileName.empty())
		return 0;
	const char *flag = 0;
	switch (mode)
	{
	case IO_Read:
	case IO_ReadText:
		flag = "rb";
		break;
	case IO_Write:
	case IO_WriteText:
		flag = "wb";
		break;
	case IO_Append:
	case IO_AppendText:
		flag = "ab+";
		break;
	case IO_ReadWrite:
	case IO_ReadWriteText:
		flag = "wb+";
		break;
	default:
		return 0;
	}
	FILE *handle = ::fopen(fileName.cstr(), flag);
	return handle;
}

BFile::BFile()
{
	member_allocate();
}
BFile::BFile(const BString &fileName)
{
	member_allocate();
	member.fileName = fileName;
}
BFile::~BFile()
{
	if (member.handle)
		::close(member.handle);
	member_release();
}

int BFile::size() const
{
	if (member.handle == 0)
		return -1;

	struct stat fs;
	::fstat(member.handle, &fs);
	return fs.st_size;
}

bool BFile::open(IOMode mode)
{
	close();
	if (member.fileName.empty())
		return false;
	member.mode = mode;
	int flags = 0;
	switch (mode)
	{
	case IO_Read:
	case IO_ReadText:
		flags = O_RDONLY;
		break;
	case IO_Write:
	case IO_WriteText:
		flags = O_CREAT | O_WRONLY;
		break;
	case IO_Append:
	case IO_AppendText:
		flags = O_CREAT | O_APPEND;
		break;
	case IO_ReadWrite:
	case IO_ReadWriteText:
		flags = O_CREAT | O_RDWR;
		break;
	default:
		return false;
	}
	member.handle = ::open(member.fileName.cstr(), flags);
	if (member.handle > 0)
	{
		struct stat s;
		fstat(member.handle, &s);
		member.size = s.st_size;
		return true;
	}
	perror(member.fileName.cstr());
	member.handle = 0;
	return false;
}
bool BFile::open(const BString &fileName, IOMode mode)
{
	member.fileName = fileName;
	if (this->checks(Signal_FileNameChanged))
		this->emit(Signal_FileNameChanged, fileName);
	return open(mode);
}
bool BFile::close()
{
	if (member.handle)
	{
		if (::close(member.handle) == -1)
		{
			printf("close file failed : %s.\n", member.fileName.cstr());
		}
		member.handle = 0;
		member.size = 0;
		return true;
	}
	return false;
}
IOMode BFile::mode() const
{
	return member.mode;
}

bool BFile::isOpened() const
{
	return member.handle != 0;
}
bool BFile::flush()
{
	return false;
}

bool BFile::seek(int offset) const
{
	if (member.handle == 0)
		return false;
	int ret = ::lseek(member.handle, offset, SEEK_SET);
	if (ret == -1)
	{
		printf("lseek file error: %s.\n", member.fileName.cstr());
		return false;
	}
	return true;
}
bool BFile::wseek(int offset)
{
	if (!member.handle)
		return false;

	int size = member.size;
	if (offset > member.size)
	{
		int count = offset - member.size;
		if (count != ::write(member.handle, &count, count))
			return false;
		member.size += count;
	}
	else
	{
		int ret = ::lseek(member.handle, offset, SEEK_SET);
		if (ret == -1)
		{
			printf("lseek file failed : %s.\n", member.fileName.cstr());
			return false;
		}
	}
	return true;
}

int BFile::offset() const
{
	if (member.handle == 0)
		return -1;
	int ret = ::lseek(member.handle, 0, SEEK_CUR);
	if (ret == -1)
	{
		printf("lseek file failed : %s.\n", member.fileName.cstr());
	}
	return ret;
}

bool BFile::read(void *buffer, int size) const
{
	if (member.handle == 0)
		return -1;
	return ::read(member.handle, buffer, size);
}
bool BFile::write(const void *buffer, int size)
{
	if (member.handle == 0)
		return -1;
	return ::write(member.handle, buffer, size);
}

bool BFile::read(BString &text) const
{
	if (!member.handle)
		return false;
	text.reset();
	while (1)
	{
		char c = 0;
		if (0 == ::read(member.handle, &c, 1))
			break;
		text.append(c);
		if (c == 0)
			break;
	}
	return text.size();
}
bool BFile::write(const BString &text)
{
	if (!member.handle)
		return false;
	if (member.mode & IO_Text)
		return write(text.cstr(), text.size());
	return write(text.cstr(), text.size() + 1);
}

bool BFile::readCStr(char *cstr, int size) const
{
	if (member.handle == 0)
		return -1;
	for (int i = 0; i < size; i++)
	{
		if (::read(member.handle, cstr + i, 1) == 0)
		{
			cstr[i] = 0;
			return i;
		}
		if (cstr[i] == 0)
			return i;
	}
	return 0;
}
bool BFile::writeCStr(const char *cstr, int size)
{
	if (member.handle == 0)
		return -1;
	if (cstr == 0)
		return false;
	if (size < 0)
		size = strlen(cstr);
	if (cstr[size] != 0)
		return false;
	return ::write(member.handle, cstr, size + 1);
}

bool BFile::readLine(char *cstr, int size) const
{
	for (int i = 0; i < size; i++)
	{
		char c = 0;
		if (0 == ::read(member.handle, &c, 1))
			return i > 0;
		cstr[i] = c;
		if (c == '\n')
			return i > 0;
	}
	return false;
}
bool BFile::readLine(BString &str) const
{
	if (!member.handle)
		return false;
	str.reset();
	while (1)
	{
		char c = 0;
		if (0 == ::read(member.handle, &c, 1))
			break;
		str.append(c);
		if (c == '\n')
			break;
	}
	return !str.empty();
}

bool BFile::readText(BString &text, int size) const
{
	if (size <= 0)
		return false;
	if (text.capacity() < size)
		text.reserve(size);
	text.reset();
	char code = 0;
	for (int i = 0; i < size; i++)
	{
		if (0 == ::read(member.handle, &code, 1))
		{
			text.reset();
			return false;
		}
		text.append(code);
		if (code == 0)
		{
			return true;
		}
	}
	return true;
}
bool BFile::writeText(const BString &text, int size)
{
	if (size < 0)
		size = text.size();
	return ::write(member.handle, text.cstr(), size + 1);
}

bool BFile::writeLine(const char *cstr, int size)
{
	if (!member.handle)
		return false;
	if (!cstr)
		return false;
	if (size == 0)
		return false;
	if (size < 0)
		size = (int)strlen(cstr);
	int count = 0;
	for (int i = 0; i < size; i++)
	{
		if (cstr[i] == '\n')
		{
			count = i + 1;
			break;
		}
	}
	if (count > 0)
	{
		if (count != ::write(member.handle, cstr, count))
			return false;
	}
	else
	{
		if (size != ::write(member.handle, cstr, size))
			return false;
		char seed = '\n';
		if (1 == ::write(member.handle, &seed, 1))
			return false;
	}
	return true;
}
bool BFile::writeLine(const BString &str)
{
	if (!member.handle)
		return false;
	int count = str.find('\n');
	if (count > 0 && count < str.size())
	{
		count += 1;
		if (count != ::write(member.handle, str.cstr(), count))
			return false;
	}
	else
	{
		if (str.size() != ::write(member.handle, str.cstr(), str.size()))
			return false;
		char seed = '\n';
		if (1 == ::write(member.handle, &seed, 1))
			return false;
	}
	return true;
}

#endif

#ifdef _WIN32

bool BFile::Exist(const BString &fileName)
{
	Attrib attrib = BEntry::Check(fileName);
	return attrib & Attrib_File;
}
bool BFile::Create(const BString &fileName, Attrib attrib)
{
	wchar_t wstr[256];
	if (fileName.contain('/'))
	{
		BString path;
		int end = fileName.find('/');
		while (end < fileName.size())
		{
			fileName.substr(path, 0, end);
			if (!BFolder::Exist(path))
			{
				if (path.hasUtf(8))
				{
					bMbsToWcs(path.cstr(), wstr);
					if (FALSE == CreateDirectoryW(wstr, 0))
						return false;
				}
				else
				{
					if (FALSE == CreateDirectoryA(path.cstr(), 0))
						return false;
				}
			}
			end = fileName.find('/', end + 1);
		}
	}
	HANDLE handle = INVALID_HANDLE_VALUE;
	if (fileName.hasUtf(8))
	{
		bMbsToWcs(fileName.cstr(), wstr);
		handle = CreateFileW(wstr, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL);
		SetFileAttributesW(wstr, ConvertFromAttrib(attrib));
	}
	else
	{
		handle = CreateFileA(fileName.cstr(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL);
		SetFileAttributesA(fileName.cstr(), ConvertFromAttrib(attrib));
	}
	if (handle == INVALID_HANDLE_VALUE)
	{
		DWORD error = GetLastError();
		return false;
	}
	CloseHandle(handle);
	return true;
}
bool BFile::Remove(const BString& fileName)
{
	if (fileName.hasUtf(8))
	{
		wchar_t wstr[256];
		bMbsToWcs(fileName.cstr(), wstr);
		return DeleteFileW(wstr);
	}
	return DeleteFileA(fileName.cstr());
}
bool BFile::Rename(const BString &fileName, const BString &newName)
{
	if (fileName.contain('/'))
	{
		BString newFileName = fileName.path();
		newFileName << '/' << newName;
		if (fileName.hasUtf(8) || newName.hasUtf(8))
		{
			wchar_t wstr0[256];
			bMbsToWcs(fileName.cstr(), wstr0);
			wchar_t wstr1[256];
			bMbsToWcs(newFileName.cstr(), wstr1);
			return ::MoveFile(wstr0, wstr1);
		}
		int result = std::rename(fileName.cstr(), newFileName.cstr());
		return result == 0;
	}
	int result = std::rename(fileName.cstr(), newName.cstr());
	return result == 0;
}

FILE *BFile::FOpen(const BString &fileName, IOMode mode)
{
	if (fileName.empty())
		return 0;
	const char *flag = 0;
	switch (mode)
	{
	case IO_Read:
	case IO_ReadText:
		flag = "rb";
		break;
	case IO_Write:
	case IO_WriteText:
		flag = "wb";
		break;
	case IO_Append:
	case IO_AppendText:
		flag = "ab+";
		break;
	case IO_ReadWrite:
	case IO_ReadWriteText:
		flag = "wb+";
		break;
	default:
		return false;
	}
	FILE *handle = 0;
	if (fileName.hasUtf(8))
	{
		wchar_t uflag[10];
		bMbsToWcs(flag, uflag);
		wchar_t uname[256];
		bMbsToWcs(fileName.cstr(), uname);
		handle = ::_wfopen(uname, uflag);
	}
	else
	{
		handle = ::fopen(fileName.cstr(), flag);
	}
	return handle;
}

BFile::BFile()
{
	member_allocate();
}
BFile::BFile(const BString &fileName)
{
	member_allocate();
	member.fileName = fileName;
}
BFile::~BFile()
{
	close();
	member_release();
}

bool BFile::isOpened() const
{
	if (member.handle)
		return true;
	return false;
}

bool BFile::open(IOMode mode)
{
	close();
	if (member.fileName.empty())
		return false;
	member.handle = FOpen(member.fileName, mode);
	if (member.handle)
	{
		member.mode = mode;
		::fseek(member.handle, 0, SEEK_END);
		member.size = ::ftell(member.handle);
		::fseek(member.handle, 0, SEEK_SET);
		return true;
	}
	return false;
}
bool BFile::open(const BString &fileName, IOMode mode)
{
	close();
	member.fileName = fileName;
	if (this->checks(Signal_FileNameChanged))
		this->emit(Signal_FileNameChanged, fileName);
	return open(mode);
}
bool BFile::close()
{
	if (member.handle)
	{
		if (std::fclose(member.handle) == 0)
		{
			member.handle = 0;
			member.size = 0;
			return true;
		}
	}
	return false;
}
bool BFile::flush()
{
	if (member.handle)
	{
		if (::fflush(member.handle) == 0)
			return true;
	}
	return false;
}

IOMode BFile::mode() const
{
	return member.mode;
}

bool BFile::seek(int offset) const
{
	if (member.handle)
	{
		if (::fseek(member.handle, offset, SEEK_SET) == 0)
			return true;
	}
	return false;
}
bool BFile::wseek(int offset)
{
	if (!member.handle)
		return false;

	int size = member.size;
	if (offset > member.size)
	{
		int count = offset - member.size;
		if (count != fwrite(&count, 1, count, member.handle))
			return false;
		member.size += count;
	}
	else
	{
		if (fseek(member.handle, offset, SEEK_SET))
			return false;
	}
	return true;
}

int BFile::offset() const
{
	if (!member.handle)
		return -1;
	return ::ftell(member.handle);
}
int BFile::size() const
{
	return member.size;
}

bool BFile::read(void *data, int size) const
{
	if (!data)
		return false;
	if (size > 0)
	{
		if (!member.handle)
			return false;
		if (size != fread(data, 1, size, member.handle))
			return false;
	}
	return true;
}
bool BFile::write(const void *data, int size)
{
	if (size > 0)
	{
		if (!member.handle || !data)
			return false;
		if (size != fwrite(data, 1, size, member.handle))
			return false;
		int offset = ftell(member.handle);
		if (offset > member.size)
			member.size = offset;
	}
	return true;
}

bool BFile::read(BString &text) const
{
	if (!member.handle)
		return false;
	text.reset();
	while (1)
	{
		char c = 0;
		if (0 == fread(&c, 1, 1, member.handle))
			break;
		text.append(c);
		if (c == 0)
			break;
	}
	return text.size();
}
bool BFile::write(const BString &text)
{
	if (!member.handle)
		return false;
	if (member.mode & IO_Text)
		return write(text.cstr(), text.size());
	return write(text.cstr(), text.size() + 1);
}

bool BFile::readCStr(char *cstr, int size) const
{
	if (!cstr || size <= 0)
		return false;
	char code = 0;
	for (int i = 0; i < size; i++)
	{
		if (!read(code))
		{
			cstr[0] = 0;
			return false;
		}
		cstr[i] = code;
		if (code == 0)
		{
			return true;
		}
	}
	return false;
}
bool BFile::writeCStr(const char *cstr, int size)
{
	if (!member.handle)
		return false;
	if (cstr == 0)
		return false;
	if (size < 0)
		size = (int)strlen(cstr);
	if (cstr[size] != 0)
		return false;
	return write(cstr, size + 1);
}

bool BFile::readLine(char *cstr, int size) const
{
	for (int i = 0; i < size; i++)
	{
		char c = 0;
		if (0 == fread(&c, 1, 1, member.handle))
			return i > 0;
		cstr[i] = c;
		if (c == '\n')
			return i > 0;
	}
	return false;
}
bool BFile::writeLine(const char *cstr, int size)
{
	if (!member.handle)
		return false;
	if (!cstr)
		return false;
	if (size == 0)
		return false;
	if (size < 0)
		size = (int)strlen(cstr);
	int count = 0;
	for (int i = 0; i < size; i++)
	{
		if (cstr[i] == '\n')
		{
			count = i + 1;
			break;
		}
	}
	if (count > 0)
	{
		if (count != fwrite(cstr, 1, count, member.handle))
			return false;
	}
	else
	{
		if (size != fwrite(cstr, 1, size, member.handle))
			return false;
		char seed = '\n';
		if (1 == fwrite(&seed, 1, 1, member.handle))
			return false;
	}
	return true;
}

bool BFile::readText(BString &text, int size) const
{
	if (size <= 0)
		return false;
	if (text.capacity() < size)
		text.reserve(size);
	text.reset();
	char code = 0;
	for (int i = 0; i < size; i++)
	{
		if (!read(code))
		{
			text.reset();
			return false;
		}
		text.append(code);
		if (code == 0)
		{
			return true;
		}
	}
	return true;
}
bool BFile::writeText(const BString &text, int size)
{
	if (size < 0)
		size = text.size();
	return write(text.cstr(), size + 1);
}

bool BFile::readLine(BString &line) const
{
	if (!member.handle)
		return false;
	line.reset();
	while (1)
	{
		char c = 0;
		if (0 == fread(&c, 1, 1, member.handle))
			break;
		line.append(c);
		if (c == '\n')
			break;
	}
	return line.size();
}
bool BFile::writeLine(const BString &line)
{
	if (!member.handle)
		return false;
	int count = line.find('\n');
	if (count > 0 && count < line.size())
	{
		count += 1;
		if (count != fwrite(line.cstr(), 1, count, member.handle))
			return false;
	}
	else
	{
		if (line.size() != fwrite(line.cstr(), 1, line.size(), member.handle))
			return false;
		char seed = '\n';
		if (1 == fwrite(&seed, 1, 1, member.handle))
			return false;
	}
	return true;
}

#endif

void BFile::setFileName(const BString &fileName)
{
	if (member.fileName != fileName)
	{
		member.fileName = fileName;
		if (this->checks(Signal_FileNameChanged))
			this->emit(Signal_FileNameChanged, fileName);
	}
}
const BString &BFile::fileName() const
{
	return member.fileName;
}
